﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using uTorrentWebAPI;

namespace uTorrentExtractor
{
    enum ExitCodes : int
    {
        Success = 0,
        DirectoryNotFound = 1,
        UnpackingFailed = 2,
        CleaningFailed = 4,
        ConfigurationError = 8,
        LogAccessDenied = 16,
        ArgumentInvalid = 32,
        UnknownError = 64
    }

    enum WinRARCodes : int
    {
        Success = 0,
        Warning = 1, // 	Warning. Non fatal error(s) occurred.
        FatalError = 2, // 	A fatal error occurred.
        CRCError = 3, // 	CRC error occurred when unpacking.
        LockedArchived = 4, // 	Attempt to modify a locked archive.
        WriteError = 5, // 	Write error.
        FileOpenError = 6, // 	File open error.
        WrongOptions = 7, // 	Wrong command line option.
        NotEnoughMemory = 8, // 	Not enough memory.
        FileCreateError = 9, // 	File create error.
        NoFilesMatching = 10, //	No files matching the specified mask and options were found.
        UserBreak = 255 // 	User break.
    }

    class Program
    {
        static int Main(string[] args)
        {
            var commandLine = new Utilities.Arguments(args);

            if (commandLine["drives"] != null)
            {
                var result = string.Join(", ", DriveInfo.GetDrives()
                    .Select(d => d.Name));

                Utilities.WinAPI.MessageBox(result, "Available Drives");
                return (int)ExitCodes.Success;
            }

            if (commandLine["view"] != null)
            {
                var logPath = commandLine["view"];
                if (logPath != null)
                    logPath = Utilities.Pathing.GetUNCPath(logPath);

                var logFile = GetLogFile(logPath);

                if (logFile != null)
                {
                    var result = Newtonsoft.Json.JsonConvert.SerializeObject(logFile);

                    Utilities.WinAPI.MessageBox(result, "Log File Content");
                }
                else
                {
                    Utilities.WinAPI.MessageBox("Cannot read log!", "Log File Content");
                }
                return (int)ExitCodes.Success;
            }

            // if no label exists or, label is not tv and not movies - exit
            if (commandLine["l"] == null ||
                (!commandLine["l"].StartsWith("tv", StringComparison.InvariantCultureIgnoreCase) &&
                    !commandLine["l"].StartsWith("movies", StringComparison.InvariantCultureIgnoreCase)))
            {
                return (int)ExitCodes.Success;
            }

            // if torrent type is not multi - exit
            if (commandLine["k"] == null ||
                commandLine["k"] != "multi")
            {
                return (int)ExitCodes.Success;
            }

            string path = commandLine["d"];
            if (path != null)
                path = Utilities.Pathing.GetUNCPath(path);

            if (path == null ||
                !Directory.Exists(path))
                return (int)ExitCodes.DirectoryNotFound;

            var log = GetLogFile(path);

            if (log.HasOpenAccessLog)
                return (int)ExitCodes.LogAccessDenied;

            log.CreateNewAccessLog(string.Join(" ", args));
            SaveLogFile(path, log);

            bool needUnpacking = false;
            bool needCleanup = false;

            var hash = commandLine["i"];
            if (string.IsNullOrEmpty(hash))
            {
                log.CloseAllAccessLogs();
                SaveLogFile(path, log);
                return (int)ExitCodes.ArgumentInvalid;
            }

            UTorrentWebClient client = new UTorrentWebClient("10.0.0.1:8080", "admin", "s5qt7ws");
            var torrent = client.Torrents.GetByHashCode(hash);
            
            if(torrent == null)
            {
                log.CloseAllAccessLogs();
                SaveLogFile(path, log);
                return (int)ExitCodes.ArgumentInvalid;
            }

            switch (torrent.SimpleStatus)
            {
                case TorrentSimpleStatus.SeedingQueued:
                case TorrentSimpleStatus.Seeding:
                case TorrentSimpleStatus.SeedingError:
                    // seeding, not downloading
                    needUnpacking = true;
                    break;
                case TorrentSimpleStatus.InactiveCompleted: // finished
                    needCleanup = true;
                    break;
                case TorrentSimpleStatus.InactiveIncomplete:
                case TorrentSimpleStatus.Error:
                case TorrentSimpleStatus.DownloadingQueued:
                case TorrentSimpleStatus.Downloading:
                case TorrentSimpleStatus.DownloadingError:
                case TorrentSimpleStatus.Paused:
                default:
                    // might still be downloading, do nothing
                    break;
            }

            /*
            if (commandLine["s"] == null)
            {
                log.CloseAllAccessLogs();
                SaveLogFile(path, log);
                return (int)ExitCodes.ArgumentInvalid;
            }

            switch (commandLine["s"])
            {
                case TorrentState.QueuedSeed:
                case TorrentState.Seeding:
                    needUnpacking = true;
                    break;
                case TorrentState.Finished:
                    needUnpacking = true;
                    needCleanup = true;
                    break;
                default:
                    break;
            }
            */

            if (needUnpacking && !log.Unpacked)
            {
                var exitCode = UnpackArchives(path, log);

                if (exitCode != ExitCodes.Success)
                {
                    log.CloseAllAccessLogs();
                    SaveLogFile(path, log);
                    return (int)exitCode;
                }
            }

            if (needCleanup && !log.Cleaned)
            {
                var exitCode = CleanupArchives(path, log);

                if (exitCode != ExitCodes.Success)
                {
                    log.CloseAllAccessLogs();
                    SaveLogFile(path, log);
                    return (int)exitCode;
                }
            }

            log.CloseAllAccessLogs();
            SaveLogFile(path, log);
            return (int)ExitCodes.Success;
        }

        public static ExitCodes UnpackArchives(string path, TorrentLog log)
        {
            if (path == null || !Directory.Exists(path))
                return ExitCodes.DirectoryNotFound;

            var directory = new DirectoryInfo(path);

            var winrarPath = System.Configuration.ConfigurationManager.AppSettings["WinRAR_Path"];

            if (winrarPath == null)
                return ExitCodes.ConfigurationError;

            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = Path.Combine(winrarPath, "unrar.exe");
            startInfo.Arguments = string.Format("x -y -r -o- \"{0}\\*.rar\" \"{0}\"", path);
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;

            try
            {
                var process = Process.Start(startInfo);

                process.WaitForExit();

                if (process.ExitCode != (int)WinRARCodes.Success &&
                    process.ExitCode != (int)WinRARCodes.NoFilesMatching)
                {
                    log.ErrorLogs.Add(new ErrorLog(string.Format("Failed to extract files from \"{0}\" (Error: {1})", path, process.ExitCode)));

                    return ExitCodes.UnpackingFailed;
                }
            }
            catch (Exception ex)
            {
                string error = "Error Unpacking: " + ex.Message + Environment.NewLine + ex.ToString();

                log.ErrorLogs.Add(new ErrorLog(error));

                return ExitCodes.UnpackingFailed;
            }


            if (directory.GetFiles("*sub*", SearchOption.AllDirectories).Length > 0)
            {
                ProcessStartInfo subStartInfo = new ProcessStartInfo();
                subStartInfo.FileName = winrarPath.TrimEnd('\\') + "\\" + "unrar.exe";
                subStartInfo.Arguments = string.Format("x -y -r -o- \"{0}\\*sub*\" \"{0}\"", path);
                subStartInfo.WindowStyle = ProcessWindowStyle.Hidden;

                try
                {
                    var subProcess = Process.Start(startInfo);

                    subProcess.WaitForExit();
                }
                catch { }
            }

            log.Unpacked = true;
            log.DateUnpacked = DateTime.Now;

            return ExitCodes.Success;
        }

        public static ExitCodes CleanupArchives(string path, TorrentLog log)
        {
            if (path == null || !Directory.Exists(path))
                return ExitCodes.DirectoryNotFound;

            var directory = new DirectoryInfo(path);

            // clean up files
            var patterns = new string[] { ".dat", "*sample*", "*.rar", "*.sfv" };

            foreach (var pattern in patterns)
                DeleteFiles(directory, pattern);

            for (int i = 0; i < 100; i++)
            {
                DeleteFiles(directory, "*.r" + string.Format("{0:00}", i));
            }

            // clean up directories
            for (int i = 0; i < 5; i++)
            {
                DeleteFolder(directory, "CD" + i.ToString());
                DeleteFolder(directory, "CD " + i.ToString());
            }
            DeleteFolder(directory, "Sample");
            DeleteFolder(directory, "Subs");

            log.Cleaned = true;
            log.DateCleaned = DateTime.Now;

            return ExitCodes.Success;
        }

        private static void DeleteFiles(DirectoryInfo directory, string pattern)
        {
            directory.GetFiles(pattern, SearchOption.AllDirectories).ToList().ForEach(f =>
            {
                try
                {
                    f.Delete();
                }
                catch { }
            });
        }

        private static void DeleteFolder(DirectoryInfo directory, string name)
        {
            directory.GetDirectories(name, SearchOption.AllDirectories).ToList().ForEach(d =>
            {
                try
                {
                    d.Delete(true);
                }
                catch { }
            });
        }

        public static TorrentLog GetLogFile(string path)
        {
            if (path == null || !Directory.Exists(path))
                return null;

            path = Path.Combine(path, "utx.log");

            TorrentLog log = null;
            if (File.Exists(path))
            {
                try
                {
                    var serializer = new Utilities.Serializer<TorrentLog>();
                    log = serializer.DeserializeObject(path);
                }
                catch { }
            }

            return log ?? new TorrentLog();
        }

        public static bool SaveLogFile(string path, TorrentLog log)
        {
            if (path == null || !Directory.Exists(path))
                return false;

            path = Path.Combine(path, "utx.log");

            try
            {
                var serializer = new Utilities.Serializer<TorrentLog>();
                serializer.SerializeObject(path, log);

                return true;
            }
            catch
            {
                return false;
            }

        }
    }
}
